<html>
    <head>
        <title>并发检查</title>
    </head>
    <body>
        <script>
            // 并发检查  Concurrency Check

                    /*
                        一、导言

                                并发检查（也称为并发控制）是指存在并发更改（多个进程、用户同时访问或更爱数据库中的相同数据）的情况下，
                                
                                用于确保数据展示一致性的特定机制。

                                有两种常用的并发控制机制/方法：

                                       1、乐观并发控制：  

                                                乐观并发控制允许多个用户尝试更新同意记录，而无需通知用户其他人也在尝试更新它。

                                                如果用户成功更新记录，其他用户需要获取当前记录的的最新更改才能进行更新。

                                                ABP的并发检查系统使用"乐观并发控制".

                                       2、悲观并发控制：

                                                悲观并发控制防止同时更新记录并使用"锁定机制"。

                                                有关详细信息，请阅读：https://www.martinfowler.com/eaaCatalog/pessimisticOfflineLock.html  
                    */


                    /*
                        二、用法

                                ★、并发戳 IHasConcurrencyStamp 接口

                                         1、实体实现 IHasConcurrencyStamp 接口，ABP会自动处理并发戳。

                                         
                                                    要为你的实体启用"并发控制"，你应该直接或间接实现 IConcurrencyStamp(并发戳) 接口。

                                                        eg:
                                                                // 并发戳接口：  (主要就是要有一个 ConcurrencyStamp 属性)
                                                                public interface IHasConcurrencyStamp 
                                                                {
                                                                    public string ConcurrencyStamp { get; set; }
                                                                }

                                                    它是"并发控制"的基本接口，只有一个名为  ConcurrencyStamp 的简单属性。

                                                    当一个新记录正在创建时，如果实体实现了 IHasConcurrencyStamp ，ABP会自动为 ConcurrencyStamp 属性生成一个唯一值。

                                                    当记录正在更新时，ABP会将实体的 ConcurrencyStamp 属性 与用户提供的 ConcurrencyStamp 值进行比较，

                                                    如果值匹配，它会自动使用新的唯一值更新 ConcurrencyStamp 属性。

                                                    如果不匹配，则抛出AbpDbConcurrencyException异常。

                                            

                                        2、工作单元中自动获取并发戳：

                                                    如果有工作单元，则需要在创建或更新时调用  SaveChangeAsync 方法"获取生成的ConcurrencyStamp".


                                                        eg:  为图书实体应用并发控制

                                                                // 为实体实现 IHasConcurrencyStamp 接口：
                                                                public class Book : Entity<Guid>, IHasConcurrencyStamp
                                                                {
                                                                    // 只要你有这个属性，ABP就会自动处理并发戳。
                                                                    public string ConcurrencyStamp { get; set; }
                                                                }


                                       3、 输入Dto、输出Dto 也应该实现 IHasConcurrencyStamp 接口，包含ConcurrencyStamp 属性。
        
                                                    你也可以让输入、输出DTO实现 IHasConcurrencyStamp 接口，这样你就可以在创建或更新实体时传递并发戳。

                                               eg:
                                                    // 输出Dto
                                                    public class BookDto : EntityDto<Guid>, IHasConcurrencyStamp 
                                                    {
                                                        // 其他属性

                                                        public string ConcurrencyStamp { get; set; }
                                                    }

                                                    // 输入Dto
                                                    public class UpdateBookDto : IHasConcurrencyStamp 
                                                    {
                                                        // 其他属性

                                                        public string ConcurrencyStamp { get; set; }
                                                    }


                                       4、在应用程序服务中自动获取并发戳：

                                                    在应用程序服务中的UpdateAsync方法中将 ConloccyStamp 输入值设置为实体的ConcurrencyStamp属性值，
                                            eg:
                                                    public class BookAppService : ApplicationService, IBookAppService 
                                                    {
                                                        //...

                                                        public virtual async Task<BookDto> UpdateAsync(Guid id, UpdateBookDto input) 
                                                        {
                                                            var book = await BookRepository.GetAsync(id);

                                                            book.ConcurrencyStamp = input.ConcurrencyStamp;

                                                            // 跟新其他属性...
                                                           
                                                            await BookRepository.UpdateAsync(book, autoSave: true);  // 使用 autoSave: true 来获取实体最新的ConcurrencyStamp
                                                        }
                                                    }

                                        上述流程之后，当多个用户尝试同时更新用一个记录时，会发生并发出哦不匹配的情况，并抛出 AbpDbConcurrencyException 异常。
                    */

                    /*
                        三、实现了IConcurrencyChecker接口的基类

                                     聚合根实体类已经实现了IHasConcurrencyStamp接口。
                                     
                                     因此，如果你是从这些基类之一派生的，则不需要手动实现IHasConcurrencyStamp接口：

                                          ①、AggregateRoot, AggregateRoot<TKey> AggregateRoot，AggregateRoot<TKey>

                                          ②、CreationAuditedAggregateRoot，CreationAuditedAggregateRoot<TKey>

                                          ③、AuditedAggregateRoot，AuditedAggregateRoot<TKey>

                                          ④、FullAuditedAggregateRoot，FullAuditedAggregateRoot<TKey>


                                    eg：
                                        为图书实体应用并发控制

                                        你可以从基类之一继承你的实体：

                                            public class Book : FullAuditedAggregateRoot<Guid>
                                            {
                                                //...
                                            }

                                        然后，输入、输出Dto

                                            public class BookDto : EntityDto<Guid>, IHasConcurrencyStamp 
                                            {
                                                //...

                                                public string ConcurrencyStamp { get; set; }
                                            }

                                            public class UpdateBookDto : IHasConcurrencyStamp 
                                            {
                                                //...

                                                public string ConcurrencyStamp { get; set; }
                                            }   

                                        应用程序服务的UpdateAsync方法中将ConloccyStamp输入值设置为实体，如下所示：
                                            public class BookAppService : ApplicationService, IBookAppService 
                                            {

                                                public virtual async Task<BookDto> UpdateAsync(Guid id, UpdateBookDto input) 
                                                {
                                                    var book = await BookRepository.GetAsync(id);

                                                    book.ConcurrencyStamp = input.ConcurrencyStamp;

                                                    // 设置其他属性的跟新 ...
                                                    // 使用参数： autoSave: true 来获取最新ConcurrencyStamp
                                                    await BookRepository.UpdateAsync(book, autoSave: true);
                                                }
                                            }

                                        之后，当多个用户尝试同时更新同一条记录时，会发生并发戳不匹配并抛出AbpDbConcurrencyException。你可以手动处理异常，也可以让ABP为你处理。

                                        如果你不手动处理异常，ABP会显示用户友好的错误消息，如下图所示。
                                            
                    */
        </script>
    </body>
</html>